package com.mokairui.threadpool.juc.semaphore;

import lombok.extern.slf4j.Slf4j;

import java.sql.*;
import java.util.concurrent.Semaphore;
import java.util.concurrent.atomic.AtomicIntegerArray;

/**
 * @Description 信号量的实际应用, 改进连接池中的等待线程的流程
 *      自定义连接池 (享元模式: 重用一定量的数据对象) 对 threadpool 中的 自定义连接池的改进
 * @Author Mokairui
 * @Since 2021/10/6
 */
@Slf4j(topic = "c.pool")
public class Pool {
    // 1. 连接池大小
    private final int poolSize;

    // 2. 连接对象数组
    private Connection[] connections;

    // 3. 连接状态数组 0 表示空闲, 1 表示繁忙
    private AtomicIntegerArray states;

    private Semaphore semaphore;

    // 4. 初始化
    public Pool(int poolSize) {
        this.poolSize = poolSize;
        // 让许可数与资源数一致
        this.semaphore = new Semaphore(poolSize);
        this.connections = new Connection[poolSize];
        this.states = new AtomicIntegerArray(new int[poolSize]);
        for (int i = 0; i < poolSize; i++) {
//            connections[i] = new MockConnection();
        }
    }

    // 5. 借连接
    public Connection borrow() {
        // 原来的实现方式, 如果没有空闲线程那么就陷入等待, 等待有线程之后再唤醒自己
        /*for (;;) {
            for (int i = 0; i < poolSize; i++) {
                // 获取空闲连接
                if (states.get(i) == 0 && states.compareAndSet(i, 0, 1)) {
                    return connections[i];
                }
            }
            // 如果没有空闲连接, 当前线程进入等待
            synchronized (this) {
                try {
                    this.wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }*/

        // 新的实现方式
        try {
            // 获取许可, 没有许可自动陷入等待
            semaphore.acquire();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        for (int i = 0; i < poolSize; i++) {
            // 获取空闲连接
            if (states.get(i) == 0 && states.compareAndSet(i, 0, 1)) {
                return connections[i];
            }
        }
        // 永远部分执行到此代码, 只是为了语法不出错
        return null;
    }

    // 6. 归还连接
    public void free(Connection conn) {
        for (int i = 0; i < poolSize; i++) {
            if (connections[i] == conn) {
                // 因为连接是一个线程独享的所以不需要进行原子操作, 而获取是可能有多个线程竞争所以需要用原子操作
                states.set(i, 0);
                // 释放连接, 归还许可
                semaphore.release();
                break;
            }
        }
//        synchronized (this) {
//            this.notifyAll();
//        }
    }
}